package cadtoolbox.graphical;

import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.NumberFormatter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;

import cadtoolbox.optimizer.Parameter;

import cadtoolbox.model.Constants;
import cadtoolbox.model.OligoGraph;
import cadtoolbox.model.SequenceVertex;

public class DataPanel<E> extends JPanel {

	private OligoGraph<SequenceVertex,E> graph;
	
	private javax.swing.JFormattedTextField jFormattedTextFieldEdge1;
    private javax.swing.JFormattedTextField jFormattedTextFieldEdge2;
    private javax.swing.JFormattedTextField jFormattedTextFieldInitConc;
    private javax.swing.JFormattedTextField jFormattedTextFieldK;
    private javax.swing.JLabel jLabelAutocat;
    private javax.swing.JLabel jLabelEdge1;
    private javax.swing.JLabel jLabelEdge2;
    private javax.swing.JLabel jLabelIncInh;
    private javax.swing.JLabel jLabelInitConc;
    private javax.swing.JLabel jLabelK;
    private javax.swing.JLabel jLabelNofNodes;
    private javax.swing.JLabel jLabelNofSeq;
    private javax.swing.JSeparator jSeparator;
    
	public DataPanel(OligoGraph<SequenceVertex,E> graph){
		this.graph = graph;
		initComponents();
		
	}
	
	public void repaint(){
		super.repaint();
		
	}
	
	private void initComponents(){
		jLabelNofSeq = new javax.swing.JLabel();
        jLabelIncInh = new javax.swing.JLabel();
        jLabelAutocat = new javax.swing.JLabel();
        jLabelNofNodes = new javax.swing.JLabel();
        
        NumberFormatter formatter = new NumberFormatter();
		//formatter.setOverwriteMode(true);
		formatter.setAllowsInvalid(true);
		formatter.setCommitsOnValidEdit(false);
        
        jLabelK = new javax.swing.JLabel();
        jFormattedTextFieldK = new javax.swing.JFormattedTextField(formatter);
        jLabelInitConc = new javax.swing.JLabel();
        jFormattedTextFieldInitConc = new javax.swing.JFormattedTextField(formatter);
        jSeparator = new javax.swing.JSeparator();
        jFormattedTextFieldEdge1 = new javax.swing.JFormattedTextField(formatter);
        jLabelEdge1 = new javax.swing.JLabel();
        jLabelEdge2 = new javax.swing.JLabel();
        jFormattedTextFieldEdge2 = new javax.swing.JFormattedTextField(formatter);

        this.setBackground(new java.awt.Color(255, 255, 255));

        jLabelNofSeq.setText("Number of sequences: 0");
        jLabelIncInh.setText("       including 0 inhibitors");
        jLabelAutocat.setText("Number of autocatalysts: 0");
        jLabelNofNodes.setText("Number of selected nodes: 0");

        jLabelK.setText("K (Dissociation constant) (nM)");
        jFormattedTextFieldK.setText("0.0");
        jFormattedTextFieldK.setFocusable(true);
        jFormattedTextFieldK.addActionListener(new ActionListener(){
        	public void actionPerformed(ActionEvent e){
        		changedK(graph.getSelected());
        	}
        });
        jFormattedTextFieldK.addFocusListener(new FocusListener(){
        	
public Set<SequenceVertex> set=new HashSet<SequenceVertex>();
        	
			@Override
			public void focusGained(FocusEvent arg0) {
				set.removeAll(set);
				if(!jFormattedTextFieldK.isVisible()){
					releaseFocus();
					return;
				}
				set.addAll(graph.getSelected());
				
			}

			@Override
			public void focusLost(FocusEvent evt) {
				// TODO Auto-generated method stub
				//double value = Double.valueOf(((javax.swing.JFormattedTextField) evt.getSource()).getText());
                if(!set.isEmpty())
				changedK(set);
			}

        	
        });

        jLabelInitConc.setText("Initial Concentration (nM)");
        jFormattedTextFieldInitConc.setText("0.0");
        jFormattedTextFieldInitConc.addActionListener(new ActionListener(){
        	public void actionPerformed(ActionEvent e){
        		changedInit(graph.getSelected());
        	}
        });
        
        jFormattedTextFieldInitConc.addFocusListener(new FocusListener(){
        	public Set<SequenceVertex> set=new HashSet<SequenceVertex>();
        	
			@Override
			public void focusGained(FocusEvent arg0) {
				set.removeAll(set);
				if(!jFormattedTextFieldInitConc.isVisible()){
					releaseFocus();
					return;
				}
				
				set.addAll(graph.getSelected());
			}

			@Override
			public void focusLost(FocusEvent evt) {
				if(!set.isEmpty())
				changedInit(set);
				
			}

        	
        });
        
        jFormattedTextFieldEdge1.setText("0.0");
        jFormattedTextFieldEdge1.addActionListener(new ActionListener(){
        	public void actionPerformed(ActionEvent e){
        		changedTemplate(false, graph.getSelected());  //Edge1 changed its template concentration
        	}
        });
        
        jFormattedTextFieldEdge1.addFocusListener(new FocusListener(){
public Set<SequenceVertex> set=new HashSet<SequenceVertex>();
        	
			@Override
			public void focusGained(FocusEvent arg0) {
				set.removeAll(set);
				if(!jFormattedTextFieldEdge1.isVisible()){
					releaseFocus();
					return;
				}
				
				set.addAll(graph.getSelected());
				
				
			}

			@Override
			public void focusLost(FocusEvent evt) {
				
				if(!set.isEmpty())
				changedTemplate(false, set);
			}

        	
        });
        jLabelEdge1.setText("Edge 1");

        jLabelEdge2.setText("Edge 2");
        jFormattedTextFieldEdge2.setText("0.0");
        jFormattedTextFieldEdge2.addActionListener(new ActionListener(){
        	public void actionPerformed(ActionEvent e){
        		changedTemplate(true, graph.getSelected()); //Edge2 changed its template concentration
        	}
        });
        jFormattedTextFieldEdge2.addFocusListener(new FocusListener(){

        	public Set<SequenceVertex> set=new HashSet<SequenceVertex>();
        	
			@Override
			public void focusGained(FocusEvent arg0) {
				set.removeAll(set);
				if(!jFormattedTextFieldEdge2.isVisible()){
					releaseFocus();
					return;
				}
				
				set.addAll(graph.getSelected());
				
			}

			@Override
			public void focusLost(FocusEvent evt) {
				if(!set.isEmpty())
				changedTemplate(true,set);
			}

        	
        });
        
        jLabelK.setVisible(false);
        jFormattedTextFieldK.setVisible(false);
        jLabelInitConc.setVisible(false);
        jFormattedTextFieldInitConc.setVisible(false);
        jLabelEdge1.setVisible(false);
        jFormattedTextFieldEdge1.setVisible(false);
        jLabelEdge2.setVisible(false);
        jFormattedTextFieldEdge2.setVisible(false);

        
        // Layout automatically generated by NetBeans IDE
        javax.swing.GroupLayout DataPanelLayout = new javax.swing.GroupLayout(this);
        this.setLayout(DataPanelLayout);
        DataPanelLayout.setHorizontalGroup(
            DataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(DataPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(DataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(DataPanelLayout.createSequentialGroup()
                        .addComponent(jLabelK, javax.swing.GroupLayout.PREFERRED_SIZE,  javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(jFormattedTextFieldK, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(DataPanelLayout.createSequentialGroup()
                        .addComponent(jLabelInitConc)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 73, Short.MAX_VALUE)
                        .addComponent(jFormattedTextFieldInitConc, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(DataPanelLayout.createSequentialGroup()
                        .addGroup(DataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(DataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                                .addComponent(jLabelNofSeq, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(jLabelIncInh, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(jLabelAutocat, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                            .addComponent(jLabelNofNodes))
                        .addGap(0, 0, Short.MAX_VALUE))
                    .addGroup(DataPanelLayout.createSequentialGroup()
                        .addComponent(jLabelEdge1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addGap(18, 18, 18)
                        .addComponent(jFormattedTextFieldEdge1, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(DataPanelLayout.createSequentialGroup()
                        .addComponent(jLabelEdge2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jFormattedTextFieldEdge2, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap())
            .addComponent(jSeparator, javax.swing.GroupLayout.Alignment.TRAILING)
        );
        DataPanelLayout.setVerticalGroup(
            DataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(DataPanelLayout.createSequentialGroup()
                .addComponent(jLabelNofSeq)
                .addGap(0, 0, 0)
                .addComponent(jLabelIncInh, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(0, 0, 0)
                .addComponent(jLabelAutocat)
                .addGap(0, 0, 0)
                .addComponent(jLabelNofNodes)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(1, 1, 1)
                .addGroup(DataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabelK)
                    .addComponent(jFormattedTextFieldK, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(0, 0, 0)
                .addGroup(DataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabelInitConc)
                    .addComponent(jFormattedTextFieldInitConc, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(1, 1, 1)
                .addGroup(DataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabelEdge1)
                    .addComponent(jFormattedTextFieldEdge1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(0, 0, 0)
                .addGroup(DataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabelEdge2)
                    .addComponent(jFormattedTextFieldEdge2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(0, 2, Short.MAX_VALUE))
        );
	}
	
	public void update(){
		jLabelNofSeq.setText("Number of sequences: "+graph.getVertexCount());
		jLabelIncInh.setText("including "+graph.getInhibitions().size()+" inhibitor"+(graph.getInhibitions().size()>1?"s":""));
		jLabelAutocat.setText("Number of autocatalysts: "+computeAutocatalysts());
		jLabelNofNodes.setText("Number of selected nodes: "+graph.getSelected().size());
		
		jLabelK.setVisible(false);
        jFormattedTextFieldK.setVisible(false);
        jLabelInitConc.setVisible(false);
        jFormattedTextFieldInitConc.setVisible(false);
        jLabelEdge1.setVisible(false);
        jFormattedTextFieldEdge1.setVisible(false);
        jLabelEdge2.setVisible(false);
        jFormattedTextFieldEdge2.setVisible(false);
        
		if(graph.getSelected().size() == 1){
			SequenceVertex seq = graph.getSelected().iterator().next();
			jLabelK.setVisible(true);
			jLabelInitConc.setVisible(true);
			jFormattedTextFieldK.setVisible(true);
			jFormattedTextFieldInitConc.setVisible(true);
			jFormattedTextFieldInitConc.setValue(seq.initialConcentration);
			jFormattedTextFieldK.setValue(graph.K.get(seq));
			
			if ( graph.getNeighbors(seq).contains(seq)){
				E autocat = graph.findEdge(seq, seq);
				
				jLabelEdge1.setText("Template "+autocat+" (nM)");
				jLabelEdge1.setVisible(true);
				jFormattedTextFieldEdge1.setValue(graph.getTemplateConcentration(autocat));
				jFormattedTextFieldEdge1.setVisible(true);
				
			}
		}
		
		if(graph.getSelected().size()==2){
			Iterator<SequenceVertex> it = graph.getSelected().iterator();
			SequenceVertex seq1 = it.next();
			SequenceVertex seq2 = it.next();
			
			E edge = graph.findEdge(seq1, seq2);
			boolean which = false;
			if (edge != null){
				jLabelEdge1.setText("Template "+edge+" (nM)");
				jLabelEdge1.setVisible(true);
				jFormattedTextFieldEdge1.setValue(graph.getTemplateConcentration(edge));
				jFormattedTextFieldEdge1.setVisible(true);	
				which = true;
			}
			
			edge = graph.findEdge(seq2, seq1);
			if (edge != null){
				if (which){
					jLabelEdge2.setText("Template "+edge+" (nM)");
					jLabelEdge2.setVisible(true);
					jFormattedTextFieldEdge2.setValue(graph.getTemplateConcentration(edge));
					jFormattedTextFieldEdge2.setVisible(true);
				} else {
					jLabelEdge1.setText("Template "+edge+" (nM)");
					jLabelEdge1.setVisible(true);
					jFormattedTextFieldEdge1.setValue(graph.getTemplateConcentration(edge));
					jFormattedTextFieldEdge1.setVisible(true);
				}
			}
		}
		this.repaint();
	}
	
	private int computeAutocatalysts(){
		int auto = 0;
	     for (SequenceVertex v : graph.getVertices()){
	    	 if (graph.getNeighbors(v).contains(v)){
	    		 auto++;
	    	 }
	     }
	     return auto;
	}
	
	private void changedK(Set<SequenceVertex> set){
		Iterator<SequenceVertex> it = set.iterator();
		SequenceVertex seq1 = it.next();
		graph.setK(seq1, Double.valueOf(jFormattedTextFieldK.getText()));
	}
	
	private void changedInit(Set<SequenceVertex> set){
		SequenceVertex vert = set.iterator().next();
		vert.setInitialConcentration(Double.valueOf(jFormattedTextFieldInitConc.getText()));
		Parameter tempSeqC = null;
		//seqK
		Enumeration<DefaultMutableTreeNode> it = ((MutableTreeNode) graph.optimizable.getChild(graph.optimizable.getRoot(), 1)).children();
		while(it.hasMoreElements()){
			DefaultMutableTreeNode next = it.nextElement();
			if(((Parameter) next.getUserObject()).target.equals(vert)){
				tempSeqC = (Parameter) next.getUserObject();
				break;
			}
		}
		if(tempSeqC != null){
			tempSeqC.currentValue = Double.valueOf(jFormattedTextFieldInitConc.getText());
		}
		//the graph doesn't see when this happens, so we replot instead.
		this.graph.replot();
		
	}
	
	public void setGraph(OligoGraph<SequenceVertex,E> graph){
		this.graph = graph;
	}
	
	//Already reploted by graph
	private void changedTemplate(boolean which, Set<SequenceVertex> set){
		// !which means Edge1 changed, which means Edge2 changed
		if(which){
			Iterator<SequenceVertex> it = set.iterator();
			SequenceVertex seq1 = it.next();
			SequenceVertex seq2 = it.next();
			graph.setTemplateConcentration(graph.findEdge(seq2, seq1), Double.valueOf(jFormattedTextFieldEdge2.getText()));
			
		} else {
			if (set.size() == 1){ // autocatalyst case
				Iterator<SequenceVertex> it = set.iterator();
				SequenceVertex seq1 = it.next();
				graph.setTemplateConcentration(graph.findEdge(seq1, seq1), Double.valueOf(jFormattedTextFieldEdge1.getText()));
				
			} else {
				Iterator<SequenceVertex> it = set.iterator();
				SequenceVertex seq1 = it.next();
				SequenceVertex seq2 = it.next();
				if(graph.findEdge(seq1, seq2)==null){
				graph.setTemplateConcentration(graph.findEdge(seq2, seq1), Double.valueOf(jFormattedTextFieldEdge1.getText()));
				} else {
					graph.setTemplateConcentration(graph.findEdge(seq1, seq2), Double.valueOf(jFormattedTextFieldEdge1.getText()));
				}
				
			}
		}
	}
	
	void releaseFocus(){
		this.getParent().requestFocus();
	}
}
